#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>
#include <stdbool.h>
#include <time.h>


#define LCD_WIDTH   800                 //LCD屏幕宽度像素大小
#define LCD_HEIGHT  480                 //LCD屏幕高度像素大小
#define NUM_MAX     10                  //允许圆的最大数量
#define NUM_COLORS  10                  //颜色列表中的颜色数量

typedef unsigned int  uint32_t; 
typedef   signed int  int32_t;

u_int16_t num = 0;                      //显示圆的数量       

uint32_t red_color = 0x00ff0000;        //红色
uint32_t green_color = 0x0000ff00;      //绿色
uint32_t blue_color = 0x000000ff;       //蓝色
uint32_t purple_color = 0x00ff00ff;     //紫色
uint32_t yellow_color = 0x00ffff00;     //黄色
uint32_t cyan_color = 0x0000ffff;       //青色
uint32_t pink_color = 0x00ffc0cb;       //粉色
uint32_t brown_color = 0x00802a2a;      //棕色
uint32_t orange_color = 0x00fa8072;     //橙色
uint32_t peacock_color = 0x0033a1c9;    //孔雀蓝

uint32_t white_color = 0x00ffffff;      //白色
uint32_t black_color = 0x00000000;      //黑色


uint32_t color_list[NUM_MAX] = {0};                 //颜色列表

                                                    //圆心列表
int32_t circle_list[NUM_MAX][2] = {200, 200,        //圆心x：200，圆心y：200
                                    600, 200,       //圆心x：600，圆心y：200
                                    500, 300,       //圆心x：500，圆心y：300
                                    300, 300,       //圆心x：300，圆心y：300
                                    400, 100,       //圆心x：400，圆心y：100
                                    400, 400,       //圆心x：400，圆心y：400
                                    100, 100,       //圆心x：100，圆心y：100
                                    700, 300,       //圆心x：700，圆心y：300
                                    100, 400,       //圆心x：100，圆心y：400
                                    700, 100};      //圆心x：700，圆心y：100

                                                   //圆的半径列表
int32_t radius_list[NUM_MAX] = {20,                //圆的半径大小：20
                                40,                //圆的半径大小：40
                                25,                //圆的半径大小：25
                                35,                //圆的半径大小：35
                                30,                //圆的半径大小：30
                                20,                //圆的半径大小：20
                                50,                //圆的半径大小：50
                                30,                //圆的半径大小：30
                                45,                //圆的半径大小：45
                                35,};              //圆的半径大小：35

                                                    //圆的移动速度列表                   
int32_t speed_list[NUM_MAX][2] = {-2, -3,           //x轴移动速度：-2，y轴移动速度-3
                                   5,  5,           //x轴移动速度： 5，y轴移动速度 5
                                  -4, -4,           //x轴移动速度：-4，y轴移动速度-4
                                   3, -3,           //x轴移动速度： 3，y轴移动速度-3
                                   0, -2,           //x轴移动速度： 0，y轴移动速度-2
                                   0,  2,           //x轴移动速度： 0，y轴移动速度 2
                                  -3,  4,           //x轴移动速度：-3，y轴移动速度 4
                                   3,  3,           //x轴移动速度： 3，y轴移动速度 3 
                                   4,  3,           //x轴移动速度： 4，y轴移动速度 3 
                                  -1, -1};          //x轴移动速度：-1，y轴移动速度-1


/***颜色列表初始化***/
void color_list_init(void)
{
    color_list[0] = red_color;
    color_list[1] = green_color; 
    color_list[2] = blue_color;
    color_list[3] = purple_color;
    color_list[4] = yellow_color;
    color_list[5] = cyan_color;
    color_list[6] = pink_color;
    color_list[7] = brown_color;
    color_list[8] = orange_color;
    color_list[9] = peacock_color;

}


/***动态创建一维数组***/
int32_t *create_1d_array(void)
{
    int16_t i = 0;
    int32_t * arr = NULL;

    arr = (int32_t *)malloc(sizeof(int32_t ) * num);

    return arr;
}


/***释放一维数组内存空间***/
void destroy_1d_array(int32_t *arr)
{
    free(arr);
    arr = NULL;
}


/***动态创建二维数组***/
int32_t **create_2d_array(void)
{
    int16_t i = 0;
    int32_t ** arr = NULL;

    arr = (int32_t **)malloc(sizeof(int32_t *) * num);
    for(i = 0; i < num; i++)
    {
        arr[i] = (int32_t *)malloc(sizeof(int32_t) * 2);
    }

    return arr;
}


/***释放二维数组内存空间***/
void destroy_2d_array(int32_t ** arr)
{
    int16_t i = 0;
    for(i = 0; i < num; i++)
    {
        free(arr[i]);
    }
    free(arr);
    arr = NULL;
}


/***设置圆起始位置、大小、移动速度以及颜色***/
void set_elements(int32_t** circle, int32_t* radius, int32_t** speed, uint32_t* color)
{
    u_int16_t i = 0;
    u_int16_t rd1 = 0;
    u_int16_t rd2 = 0;
    int32_t   tmp_val_0 = 0;
    int32_t   tmp_val_1 = 0;

    for(i = 0; i < NUM_MAX/2; i++)
    {
        rd1 = rand() % NUM_MAX;
        rd2 = rand() % NUM_MAX;

        tmp_val_0 = circle_list[rd1][0];
        tmp_val_1 = circle_list[rd1][1];
        circle_list[rd1][0] = circle_list[rd2][0];
        circle_list[rd1][1] = circle_list[rd2][1];
        circle_list[rd2][0] = tmp_val_0;
        circle_list[rd2][1] = tmp_val_1;

        tmp_val_0 = speed_list[rd1][0];
        tmp_val_1 = speed_list[rd1][1];
        speed_list[rd1][0] = speed_list[rd2][0];
        speed_list[rd1][1] = speed_list[rd2][1];
        speed_list[rd2][0] = tmp_val_0;
        speed_list[rd2][1] = tmp_val_1;

        tmp_val_0 = (int32_t)radius_list[rd1];
        radius_list[rd1] = radius_list[rd2];
        radius_list[rd2] = (uint32_t)tmp_val_0;

        tmp_val_0 = color_list[rd1];
        color_list[rd1] = color_list[rd2];
        color_list[rd2] = tmp_val_0;
    }

    for(i = 0; i < num; i++) 
    {
        circle[i][0] = circle_list[i][0];
        circle[i][1] = circle_list[i][1];

        speed[i][0] = speed_list[i][0];
        speed[i][1] = speed_list[i][1];

        radius[i] = radius_list[i];

        color[i] = color_list[i];
    }

}


/***随即选取并返回颜色列表中的一种颜色***/
uint32_t my_rand(void)
{
    int32_t rd = rand() % NUM_COLORS;

    return color_list[rd];
}


/***对LCD屏幕的某一像素进行颜色rgb赋值***/
/***
 * 参数：
 * x,y：该像素在LCD屏幕的位置坐标值
 * color：该像素所要显示的颜色
 * lcd_ptr：操作LCD屏幕的指针
 * ***/
void lcd_draw_point(uint32_t x, uint32_t y, uint32_t color, uint32_t *lcd_ptr)
{
    if(x < 0 || x >= LCD_WIDTH || y < 0 || y >= LCD_HEIGHT)
    {
        printf("drawing point outside of screen.");
        exit(1);
    }

    *(lcd_ptr + y * LCD_WIDTH + x) = color;
}


/***初始化LCD屏幕背景颜色***/
void blackground_init(uint32_t color, uint32_t *lcd_ptr)
{
    uint32_t cur_x = 0;
    uint32_t cur_y = 0;
    uint32_t blackground_color = 0;

    while(cur_y < LCD_HEIGHT)
    {
        while(cur_x < LCD_WIDTH)
        {
            // //绘制黑白格子
            // if((cur_y / 20 % 2 == 0 && cur_x / 20 % 2 == 0) || (cur_y / 20 % 2 == 1 && cur_x / 20 % 2 == 1))
            // {
            //     blackground_color = white_color;       
            // }
            // else
            // {
            //     blackground_color = black_color;
            // }

            // lcd_draw_point(cur_x, cur_y, blackground_color, lcd_ptr);

            lcd_draw_point(cur_x, cur_y, color, lcd_ptr);

            cur_x++;
        }
        cur_x = 0;
        cur_y++;
    }
}


/***绘制背景颜色以及圆***/
/***
 * 参数：
 * circle：圆心坐标，如circle[0][0]代表第一个圆的圆心横坐标值x，circle[0][1]代表第一个圆的圆心纵坐标值y
 * radius：圆的半径大小
 * color： 圆的颜色
 * lcd_ptr：操作LCD屏幕的指针
 * ***/
void lcd_draw_circle(int32_t** circle, int32_t* radius, uint32_t* color, uint32_t *lcd_ptr)
{
    int16_t i = 0;
    int32_t x_begin = 0;
    int32_t y_begin = 0;
    int32_t x_end = 0;
    int32_t y_end = 0;
    int32_t cur_x = 0;
    int32_t cur_y = 0;

    for(i = 0; i < num; i++)
    {
        if(circle[i][0] < 0 || circle[i][0] >= LCD_WIDTH 
            || circle[i][1] < 0 || circle[i][1] >= LCD_HEIGHT
            || radius[i] < 1 || radius[i] > LCD_HEIGHT/2)
        {
            printf("drawing point outside of screen.");
            exit(1);
        }

        if(circle[i][0] - radius[i] < 0)
        {
            circle[i][0] = radius[i];
        }
        if(circle[i][1] - radius[i] < 0)
        {
            circle[i][1] = radius[i];
        }
        if(circle[i][0] + radius[i] >= LCD_WIDTH)
        {
            circle[i][0] = LCD_WIDTH -1 - radius[i];
        }
        if(circle[i][1] + radius[i] >= LCD_HEIGHT)
        {
            circle[i][1] = LCD_HEIGHT -1 - radius[i];
        }

        x_begin = circle[i][0] - radius[i];
        y_begin = circle[i][1] - radius[i];
        x_end = circle[i][0] + radius[i];
        y_end = circle[i][1] + radius[i];

        for(cur_y = y_begin; cur_y <= y_end; cur_y++)
        {
            for(cur_x = x_begin; cur_x <= x_end; cur_x++)
            {
                lcd_draw_point(cur_x, cur_y, white_color, lcd_ptr);
                if(pow((double)(cur_x - circle[i][0]), 2) + pow((double)(cur_y - circle[i][1]), 2) <= pow((double)(radius[i]), 2))
                {
                    lcd_draw_point(cur_x, cur_y, color[i], lcd_ptr);
                }
            }
        }
    }   

}


/***实现动态圆、圆碰撞反弹的功能***/
/***
 * 参数：
 * circle：圆心坐标，如circle[0][0]代表第一个圆的圆心横坐标值x，circle[0][1]代表第一个圆的圆心纵坐标值y
 * radius：圆的半径大小
 * color： 圆的颜色
 * speed： 圆的移动速度，如speed[0][0]代表第一个圆的横轴方向x的移动速度，speed[0][1]代表第一个圆的纵轴方向y的移动速度
 * lcd_ptr：操作LCD屏幕的指针
 * ***/
void lcd_runing_circlr(int32_t** circle, int32_t* radius, int32_t** speed, uint32_t* color, uint32_t *lcd_ptr)
{
    int16_t i = 0;
    int16_t j = 0;

    int32_t tmp_x_speed = 0;
    int32_t tmp_y_speed = 0;

    while(true)
    {
        //绘制圆
        lcd_draw_circle(circle, radius, color, lcd_ptr);

        for(i = 0; i < num; i++)
        {
            for(j = i + 1; j < num; j++)
            {
                //判断圆与圆是否发生了碰撞
                double dis = pow((double)(circle[i][0] - circle[j][0]), 2) + pow((double)(circle[i][1] - circle[j][1]), 2);
                if(dis <= pow((double)(radius[i] + radius[j]), 2))
                {                                
                    tmp_x_speed = speed[i][0];
                    tmp_y_speed = speed[i][1];
                    speed[i][0] = speed[j][0];
                    speed[i][1] = speed[j][1];
                    speed[j][0] = tmp_x_speed;
                    speed[j][1] = tmp_y_speed;

                    if(speed[i][0] <= 0 && speed[j][0] <= 0 && (speed[i][1] < 0 && speed[j][1] < 0
                                                            || speed[i][1] > 0 && speed[j][1] > 0))
                    {
                        if(circle[i][0] < circle[j][0])
                        {
                            speed[j][0] = -speed[j][0];
                        }
                        else
                        {
                            speed[i][0] = -speed[i][0];
                        }
                    }

                    if(speed[i][0] >= 0 && speed[j][0] >= 0 && (speed[i][1] < 0 && speed[j][1] < 0
                                                            || speed[i][1] > 0 && speed[j][1] > 0))
                    {

                        if(circle[i][0] < circle[j][0])
                        {
                            speed[i][0] = -speed[i][0];
                        }
                        else
                        {
                            speed[j][0] = -speed[j][0];
                        }
                    }

                    if(speed[i][1] < 0 && speed[j][1] < 0 || speed[i][1] > 0 && speed[j][1] > 0)
                    {
                        if(speed[i][0] > 0 && speed[j][0] < 0 && circle[i][0] < circle[j][0]
                            || speed[i][0] < 0 && speed[j][0] > 0 && circle[i][0] > circle[j][0])
                        {
                            speed[i][0] = speed[i][0] + speed[j][0];
                            speed[j][0] = speed[i][0] - speed[j][0];
                            speed[i][0] = speed[i][0] - speed[j][0];

                            speed[i][1] = speed[i][1] + speed[j][1];
                            speed[j][1] = speed[i][1] - speed[j][1];
                            speed[i][1] = speed[i][1] - speed[j][1];
                        }
                    }
                    
                    if(speed[i][0] > 0 && speed[j][0] > 0 || speed[i][0] < 0 && speed[j][0] < 0)
                    {
                        if(speed[i][1] > 0 && speed[j][1] < 0 && circle[i][1] < circle[j][1]
                            || speed[i][1] < 0 && speed[j][1] > 0 && circle[i][1] > circle[j][1])
                        {
                            speed[i][0] = speed[i][0] + speed[j][0];
                            speed[j][0] = speed[i][0] - speed[j][0];
                            speed[i][0] = speed[i][0] - speed[j][0];

                            speed[i][1] = speed[i][1] + speed[j][1];
                            speed[j][1] = speed[i][1] - speed[j][1];
                            speed[i][1] = speed[i][1] - speed[j][1];
                        }
                    }
                    

                    color[i] = my_rand();
                    color[j] = my_rand();

                    break;
                }
            }

            //判断圆是否发生了碰壁
            if(circle[i][0] <= radius[i] || circle[i][0] + radius[i] >= LCD_WIDTH - 1)
            {
                speed[i][0] = -speed[i][0];

                color[i] = my_rand();                
            }
            if(circle[i][1] <= radius[i] || circle[i][1] + radius[i] >= LCD_HEIGHT - 1)
            {
                speed[i][1] = -speed[i][1];
                
                color[i] = my_rand();
            }

            //改变圆的中心坐标以实现动态圆
            circle[i][0] += speed[i][0];
            circle[i][1] += speed[i][1];

        }
    }   
}


int main()
{
    int fd = 0;
    int ret = 0;

    int32_t** mycircle;
    int32_t* myradius;
    uint32_t* mycolor;
    int32_t** myspeed;
    
    uint32_t *mylcd_ptr = NULL;                 //操作LCD屏幕的指针

    /***输入显示圆的个数***/
    while(num <= 0 || num > 10)
    {
        printf("Please input the number of circles: ");
        scanf("%d", &num);

        if(num <= 0 || num > 10)
        {
            printf("Input error.\n");
        }
    
    }

    /***开辟一维数组空间***/
    myradius = create_1d_array();
    mycolor = (uint32_t*)create_1d_array();

    /***开辟二维数组空间***/
    mycircle = create_2d_array();
    myspeed = create_2d_array();
   
    srand((unsigned int)time(NULL));            //随即种子 

    /***初始化***/
    color_list_init();

    /***设置圆起始位置、大小、移动速度以及颜色***/
    set_elements(mycircle, myradius, myspeed, mycolor);       


    fd = open("/dev/fb0", O_RDWR);              //打开LCD文件
    if(fd < 0)
    {
        perror("open lcd file failed");
        exit(1);
    }

                                                //LCD文件的内存映射
    mylcd_ptr = (uint32_t*)mmap(NULL, LCD_HEIGHT * LCD_WIDTH * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    blackground_init(white_color, mylcd_ptr);
    // lcd_draw_circle(300, 200, 100, red_color, mylcd_ptr);

    lcd_runing_circlr(mycircle, myradius, myspeed, mycolor, mylcd_ptr);

    munmap(mylcd_ptr, LCD_HEIGHT * LCD_WIDTH * 4);      //内存映射的释放

     /***释放一维数组空间***/
    destroy_1d_array(myradius);
    destroy_1d_array(mycolor);

    /***释放二维数组空间***/
    destroy_2d_array(mycircle);
    destroy_2d_array(myspeed);

    ret = close(fd);                                    //关闭LCD文件
    if(ret < 0)
    {
        perror("close lcd file failed");
        exit(1);
    }

    return 0;
}
